package com.java.tutorials.mongodb;

import com.java.tutorials.po.UserInfo;
import com.mongodb.*;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.result.DeleteResult;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import static com.mongodb.client.model.Filters.*;
import static com.mongodb.client.model.Projections.exclude;
import static com.mongodb.client.model.Projections.excludeId;
import static com.mongodb.client.model.Sorts.descending;
import static com.mongodb.client.model.Updates.inc;
import static com.mongodb.client.model.Updates.set;

/**
 * Created by xuzhike on 2016/2/13.
 */
public class MongoDBDemo2 {
    MongoClient mongoClient = null;
    String collectionName = "person_basic";
    DB db = null;
    MongoDatabase database = null;
    String USER_COLLECTION = "USER_INFO";
    MongoCollection<Document> collection = null;
    MongoCollection<Document> users = null;

    DBCursor cursor = null;


    //1: connection
    public void connection(){
        //The MongoClient instance actually represents a pool of connections to the database; you will only need one instance of class MongoClient even with multiple threads.

        // To directly connect to a single MongoDB server
// (this will not auto-discover the primary even if it's a member of a replica set)
        MongoClient mongoClient = new MongoClient();

// or
        MongoClient mongoClient2 = new MongoClient( "localhost" );

// or
        MongoClient mongoClient3 = new MongoClient( "localhost" , 27017 );

// or, to connect to a replica set, with auto-discovery of the primary, supply a seed list of members
        MongoClient mongoClient4 = new MongoClient(
                Arrays.asList(new ServerAddress("localhost", 27017),
                        new ServerAddress("localhost", 27018),
                        new ServerAddress("localhost", 27019)));

// or use a connection string
        MongoClientURI connectionString = new MongoClientURI("mongodb://localhost:27017,localhost:27018,localhost:27019");
        MongoClient mongoClient5 = new MongoClient(connectionString);

        MongoDatabase database = mongoClient.getDatabase("mydb");
        //At this point, the database object will be a connection to a MongoDB server for the specified database.
    }


    @Before
    public void init(){
        mongoClient = new MongoClient("localhost", 27017);
        collection = mongoClient.getDatabase("mongodb_test").getCollection(collectionName);
        database = mongoClient.getDatabase("mongodb_test"); //获取数据库实例
//        database.createCollection(USER_COLLECTION);
        users = database.getCollection(USER_COLLECTION);
        db = mongoClient.getDB("mongodb_test");
//        cursor = database.getCollection(collectionName).find();
//        mongoClient.get
//        Mongo mongo = new Mongo();
    }

    @Test
    public void dropCollection(){
        database.getCollection(USER_COLLECTION).drop();
    }

    @After
    public void close(){
        mongoClient.close();
    }

    @Test
    public void insertWithPureAPI(){
        List<Document> userList = new ArrayList<Document>();

        for(int i=10;i<20;i++) {
            UserInfo userInfo = UserInfo.create("userName-"+i, "id-"+i);
            Document doc = new Document();
            doc.append("houseNo", userInfo.getAddress().getHouseNo()+"-"+i);
            doc.append("city", userInfo.getAddress().getCity()+"-"+i);
            doc.append("country", userInfo.getAddress().getCountry()+"-"+i);
            doc.append("phoneNum", userInfo.getAddress().getPhoneNum()+"-"+i);
            doc.append("street", userInfo.getAddress().getStreet()+"-"+i);

            Document user = new Document();
            user.append("address", doc);
            user.append("id",userInfo.getId());
            user.append("age", userInfo.getAge()+i);
            user.append("name", userInfo.getName());
            user.append("height", userInfo.getHeight()); //Note how addrObj is mapped here
            userList.add(user);
        }
        users.insertMany(userList);
        //users.insertOne(user);

    }

    @Test
    public void count(){
        System.out.println("user count = "+users.count());
    }

    @Test
    public void findFirst(){
        Document myDoc = users.find().first();
        System.out.println(myDoc.toJson());
        //The _id element has been added automatically by MongoDB to your document and your value will differ from that shown. MongoDB reserves field names that start with “_” and “$” for internal use.
    }

    //To retrieve all the documents in the collection, we will use the find() method. The find() method returns a FindIterable instance
    // that provides a fluent interface for chaining or controlling find operations. Use the iterator() method to get an iterator
    // over the set of documents that matched the query and iterate. The following code retrieves all documents in
    // the collection and prints them out (101 documents):
    @Test
    public void findAll(){

        MongoCursor<Document> cursor = users.find().iterator();
        try {
            while (cursor.hasNext()) {
                System.out.println(cursor.next().toJson());
            }
        } finally {
            cursor.close();
        }
    }

    @Test
    public void findSingleDocWithQueryFilter(){
        Document myDoc = users.find(eq("id","userName-4_id=id-4")).first();
        System.out.println(myDoc.toJson());
    }

    @Test
    public void findDocSetWithQuery(){
        // now use a range query to get a larger subset
        Block<Document> printBlock = new Block<Document>() {
            @Override
            public void apply(final Document document) {
                System.out.println(document.toJson());
            }
        };
        users.find(gt("age", 40)).forEach(printBlock);
        users.find(and(gt("age", 40), lte("age", 46))).forEach(printBlock);
    }

    @Test
    public void sorting(){
        Document myDoc = users.find(exists("age")).sort(descending("age")).first();
        System.out.println(myDoc.toJson());
    }

    //Sometimes we don’t need all the data contained in a document, the Projections helpers help build the projection parameter for the find operation.
    // Below we’ll sort the collection, exclude the _id field by using the Projections.excludeId and output the first matching document:
    @Test
    public void projectFields(){
        Document myDoc = users.find().projection(excludeId()).projection(exclude("name")).first();
        System.out.println(myDoc.toJson());
    }

    //To update at most a single document (may be 0 if none match the filter), use the updateOne method to specify the filter and the update document.
    // Here we use the Updates.set helper to update the first document
    // that meets the filter i equals 10 and set the value of i to 110:
    //collection.updateOne(eq("i", 10), set("i", 110));
    @Test
    public void updateOneDoc(){
        users.updateOne(eq("age",40),set("age",42));
    }

    //To update all documents matching the filter use the updateMany method.
    //Here we use the Updates.inc helper to increment the value of i by 100 where i is less than 100.
    //UpdateResult updateResult = collection.updateMany(lt("i", 100), inc("i", 100));
    //System.out.println(updateResult.getModifiedCount());
    //The update methods return an UpdateResult which provides information about the operation including the number of documents modified by the update.
    @Test
    public void updateMany(){
        UpdateResult updateResult = users.updateMany(lt("age", 42), inc("age", 43));
        System.out.println(updateResult.getModifiedCount());
    }

    @Test
    public void deleteOne(){
        this.count();
        users.deleteOne(eq("age", 83));
        this.count();
    }

    @Test
    public void deleteMany(){
        this.count();
        DeleteResult deleteResult = users.deleteMany(gte("age", 80));
        System.out.println(deleteResult.getDeletedCount());
        this.count();
    }

    //These new commands allow for the execution of bulk insert/update/delete operations. There are two types of bulk operations:
    //1: Ordered bulk operations.
    //    Executes all the operation in order and error out on the first write error.
    //2: Unordered bulk operations.
    //   Executes all the operations and reports any the errors.
    //   Unordered bulk operations do not guarantee order of execution.
    @Test
    public void bulkOperatoin(){
// 2. Ordered bulk operation - order is guarenteed
//        collection.bulkWrite(Arrays.asList(new InsertOneModel<>(new Document("_id", 4)),
//                        new InsertOneModel<>(new Document("_id", 5)),
//                        new InsertOneModel<>(new Document("_id", 6)),
//                        new UpdateOneModel<>(new Document("_id", 1),
//                                new Document("$set", new Document("x", 2))),
//                        new DeleteOneModel<>(new Document("_id", 2)),
//                        new ReplaceOneModel<>(new Document("_id", 3),
//                                new Document("_id", 3).append("x", 4))));


        // 2. Unordered bulk operation - no guarantee of order of operation
//        collection.bulkWrite(
//                Arrays.asList(new InsertOneModel<>(new Document("_id", 4)),
//                        new InsertOneModel<>(new Document("_id", 5)),
//                        new InsertOneModel<>(new Document("_id", 6)),
//                        new UpdateOneModel<>(new Document("_id", 1),
//                                new Document("$set", new Document("x", 2))),
//                        new DeleteOneModel<>(new Document("_id", 2)),
//                        new ReplaceOneModel<>(new Document("_id", 3),
//                                new Document("_id", 3).append("x", 4))),
//                new BulkWriteOptions().ordered(false));
    }

    @Test
    public void listIndex(){
        for (final Document index : users.listIndexes()) {
            System.out.println(index.toJson());
        }
    }

    @Test
    public void runCommand(){
        Document buildInfo = database.runCommand(new Document("buildInfo", 1));
        System.out.println(buildInfo);
    }
}
